package com.qfmy.service.impl.assistant;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.multimodalconversation.AudioParameters;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.qfmy.config.DashScopeConfig;
import com.qfmy.mapper.assistant.ChatMapper;
import com.qfmy.service.assistant.ChatService;
import com.qfmy.vo.assistant.AiAnswerVo;
import common.exception.BaseException;
import common.login.LoginUser;
import common.login.LoginUserHolder;
import common.result.ResultCodeEnum;
import io.reactivex.Flowable;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import model.entity.assistant.AiAnswer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat;

import java.io.File;
import java.nio.ByteBuffer;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 清风明月
 * @Package com.qfmy.service.impl.assistant
 * @date 2025/5/18
 * @description 智能助手
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class ChatServiceImpl implements ChatService {

    private static final String CHAT_HISTORY_KEY_PREFIX = "chat_history:";;
    /**
     * 注入mapper
     */
    @Autowired
    private ChatMapper chatMapper;
    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 注入阿里的模型配置
     */
    @Autowired
    private DashScopeConfig dashScopeConfig;
    /**
     * 生成器
     */
    private Generation generation;

    /**
     * 初始化生成器
     */
    @PostConstruct
    public void init() {
        generation = new Generation();
    }

    /**
     * 文本对话
     *
     * @param prompt
     * @param isThink
     * @param search
     * @return
     */
    @Override
    public Flux<String> text(String prompt, Boolean isThink, Boolean search) {
        //获取用户id
        Long userId = getUserId();

        // 将用户的消息添加到 Redis 中的对话历史
        saveToRedisChatHistory(prompt, Role.USER.getValue(), userId);

        // 从 Redis 获取该用户的对话历史（用于上下文）
        ArrayDeque<Message> history = getRedisChatHistory(userId);

        return Flux.create(emitter -> {
            // 用于构建AI的完整回复
            StringBuilder aiResponseBuilder = new StringBuilder();
            try {
                // 1. 构建请求参数
                GenerationParam param = GenerationParam.builder()
                        .model(dashScopeConfig.getChatModel())
                        .apiKey(dashScopeConfig.getApiKey())
                        .messages(history.stream().toList())
                        .toolChoice("auto")
                        .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                        .incrementalOutput(true) // 开启增量输出
                        .enableSearch(search) // 开启搜索引擎
                        .enableThinking(isThink)// 开启思考模式
                        .build();

                // 2. 调用流式API
                Flowable<GenerationResult> flowable = generation.streamCall(param);

                // 3. 将Flowable转换为Flux
                flowable.subscribe(
                        result -> processResult(result, emitter, aiResponseBuilder),
                        emitter::error,
                        () -> {
                            // 在流式响应完成后保存AI的回复内容
                            saveAnswer(aiResponseBuilder.toString(), prompt,null,null,userId);
                            emitter.complete();
                            // 将AI的消息添加到历史记录
                            Message aiMessage = new Message();
                            aiMessage.setRole(Role.ASSISTANT.getValue());
                            aiMessage.setContent(aiResponseBuilder.toString());
                            history.add(aiMessage);
                        }
                );

            } catch (ApiException | InputRequiredException | NoApiKeyException e) {
                emitter.error(e);
            }
        });

    }



    /**
     * 图片对话
     *
     * @param image
     * @param prompt
     * @return
     */
    @Override
    public Flux<String> image(String[] image, String prompt) {
        //获取用户id
        Long userId = getUserId();
        return Flux.create(fluxSink -> {
            MultiModalConversation conversation = new MultiModalConversation();
            List<Map<String, Object>> contentList = new ArrayList<>();

            // 创建文本消息
            Map<String, Object> textMessage = new HashMap<>();
            textMessage.put("text", prompt);
            contentList.add(textMessage);

            // 遍历图像数组，为每张图片创建一个独立的消息
            for (String img : image) {
                Map<String, Object> imgMessage = new HashMap<>();
                imgMessage.put("image", img);
                contentList.add(imgMessage);
            }

            //保存图片
            saveAnswer(prompt,null,image,null,userId);

            MultiModalMessage userMessage = MultiModalMessage.builder()
                    .role(Role.USER.getValue())
                    .content(contentList)
                    .build();

            // 用于保存完整的回复内容
            StringBuilder completeResponse = new StringBuilder();

            try {
                MultiModalConversationParam param = buildMultiModalConversationParam(userMessage);
                Flowable<MultiModalConversationResult> resultFlowable = conversation.streamCall(param);
                resultFlowable.subscribe(result -> {
                    List<Map<String, Object>> content = result.getOutput().getChoices().get(0).getMessage().getContent();
                    if (content != null && !content.isEmpty()) {
                        Object text = content.get(0).get("text");
                        if (text != null && !text.toString().isEmpty()) {
                            String responseText = text.toString();
                            fluxSink.next(responseText);

                            // 将每次接收到的部分回复追加到completeResponse中
                            completeResponse.append(responseText);
                        }
                    }
                }, error -> {
                    log.error("Error occurred during multimodal conversation: {}", error.getMessage());
                    fluxSink.error(error);
                }, () -> {
                    // 在流完成时保存完整的回复内容到数据库
                    saveAnswer(completeResponse.toString(), prompt, null,null,userId);
                    fluxSink.complete();
                });
            } catch (Exception e) {
                log.error("Unexpected error occurred during multimodal conversation: {}", e.getMessage());
                fluxSink.error(e);
            }
        });
    }

    /**
     * 音频对话
     *
     * @param audio
     * @return
     */
    @Override
    public byte[] audio(String audio) {
        SpeechSynthesizer synthesizer = new SpeechSynthesizer();
        SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                 .apiKey(dashScopeConfig.getApiKey())
                .model(dashScopeConfig.getModelTts())
                .text(audio)
                .sampleRate(48000)
                .format(SpeechSynthesisAudioFormat.MP3)
                .build();
        ByteBuffer call = synthesizer.call(param);
        byte[] audioData = new byte[call.remaining()]; // 创建一个足够大的数组
        call.get(audioData); // 将 ByteBuffer 中的数据复制到数组
        return audioData;
    }

    /**
     * 获取历史记录
     * @return
     */
    @Override
    public List<AiAnswer> history() {
        //获取用户id
        Long userId = getUserId();
        List<AiAnswer> history = chatMapper.history(userId);
        return history;
    }

    /**
     * 删除历史记录
     * @param time
     */
    @Override
    public void deleteByTime(String time) {
        //获取用户id
        Long userId = getUserId();
        chatMapper.deleteByTime(time,userId);
    }

    /**
     * 根据时间获取历史记录
     * @param time
     * @return
     */
    @Override
    public List<AiAnswerVo> getHistoryByTime(String time) {
        //获取用户id
        Long userId = getUserId();
        List<AiAnswer> history = chatMapper.getHistoryByTime(time,userId);
        //拷贝数据
        List<AiAnswerVo> aiAnswerVos =  Copy(history);
        return aiAnswerVos;
    }

    /**
     * 清空缓存
     */
    @Override
    public void clearCache() {
        //获取用户id
        Long userId = getUserId();
        //获取当前日期
        String formattedDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String key = CHAT_HISTORY_KEY_PREFIX + userId + ":chatHistory" + ":" + formattedDate;
        redisTemplate.delete(key);
    }

    /**
     * 语音转文字
     * @param voice
     * @return
     */
    @Override
    public String voice(String voice) {
        // 创建Recognition实例
        Recognition recognizer = new Recognition();

        // 创建RecognitionParam
        RecognitionParam param =
                RecognitionParam.builder()
                        .apiKey(dashScopeConfig.getApiKey())
                        .model(dashScopeConfig.getModelVoice()) // 使用实时版模型
                        .format("mp3")                  // 音频文件格式
                        .sampleRate(16000)              // 采样率
                        .parameter("language_hints", new String[]{"zh", "en"}) // 语言提示
                        .build();

        try {
            // 调用识别接口
            String recognitionResult = recognizer.call(param, new File(voice));
            return recognitionResult;
        } catch (Exception e) {
            // 捕获异常并返回错误信息
            e.printStackTrace();
            return "识别失败：" + e.getMessage();
        }
    }


    /**
     * 语音转文字流
     *
     * @param localFilePath 本地语音文件路径
     * @return 转换后的文字内容
     */
    @Override
    public String voiceStream(String localFilePath) {
        // 检查输入路径是否为空
        if (localFilePath == null || localFilePath.trim().isEmpty()) {
            throw new IllegalArgumentException("语音文件路径不能为空");
        }

        // 创建多模态对话对象
        MultiModalConversation conv = new MultiModalConversation();

        // 构建用户消息，包含语音文件和查询文本
        MultiModalMessage userMessage = MultiModalMessage.builder()
                .role(Role.USER.getValue())
                .content(Arrays.asList(
                        new HashMap<String, Object>() {{
                            put("audio", localFilePath);
                        }},
                        new HashMap<String, Object>() {{
                            put("text", "对我音频里面的问题进行回答");
                        }}
                ))
                .build();

        // 构建多模态对话参数
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                .model(dashScopeConfig.getModelVoiceInstruct())
                .apiKey(dashScopeConfig.getApiKey())
                .message(userMessage)
                .build();

        try {
            // 调用多模态对话接口
            MultiModalConversationResult result = conv.call(param);
            return result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
        } catch (Exception e) {
            // 捕获异常并打印错误信息
            System.err.println("语音转文字失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 语音对话
     * @param text
     * @return
     */
    @Override
    public String tts(String text) {
        MultiModalConversation conv = new MultiModalConversation();
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                .model(dashScopeConfig.getModelVoiceToVoice())
                .apiKey(dashScopeConfig.getApiKey())
                .text(text)
                .voice(AudioParameters.Voice.CHERRY)
                .build();
        try {
            MultiModalConversationResult result = conv.call(param);
            return result.getOutput().getAudio().getUrl();
        } catch (Exception e) {
            log.error("语音对话失败: {}", e.getMessage());
            throw new BaseException(ResultCodeEnum.APP_VOICE_ERROR);
        }
    }

    /**
     * 拷贝数据
     * @param history
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private List<AiAnswerVo> Copy(List<AiAnswer> history) {
        if( history == null){
            return new ArrayList<>();
        }
        //创建一个新集合
        List<AiAnswerVo> aiAnswerVos = new ArrayList<>();
        history.stream().forEach(aiAnswer -> {
            AiAnswerVo aiAnswerVo = new AiAnswerVo();
            //获取用户的回复
            String userContent = aiAnswer.getUserContent();
            aiAnswerVo.setType(1);
            aiAnswerVo.setContent(userContent);
            //生成随机范围为Int最大值
            int newRandom = new Random().nextInt(Integer.MAX_VALUE);
            aiAnswerVo.setId(newRandom);
            aiAnswerVo.setError(false);
            aiAnswerVo.setLoading(false);
            //判断是否使用了图片
            if(aiAnswer.getImage() != null)
            {
                //将图片进行分割
                String[] split = aiAnswer.getImage().split(",");
                if(split != null && split.length > 0){
                    aiAnswerVo.setImage(split);
                }else
                {
                    aiAnswerVo.setImage(new String[]{aiAnswer.getImage()});
                }
                aiAnswerVos.add(aiAnswerVo);
            }else{
                aiAnswerVos.add(aiAnswerVo);
                //创建一个ai的回复
                AiAnswerVo aiAnswerVo1 = new AiAnswerVo();
                aiAnswerVo1.setType(0);
                //生成随机范围为Int最大值
                newRandom = new Random().nextInt(Integer.MAX_VALUE);
                aiAnswerVo1.setId(newRandom);
                //获取ai回复的内容
                String aiContent = aiAnswer.getAiContent();
                aiAnswerVo1.setContent(aiContent);
                aiAnswerVo1.setError(false);
                aiAnswerVo1.setLoading(false);
                aiAnswerVos.add(aiAnswerVo1);
            }

        });
        return aiAnswerVos;
    }


    /**
     * 构建多模态对话参数
     * @param userMessage
     * @return
     */
    private MultiModalConversationParam buildMultiModalConversationParam(MultiModalMessage message) {
        return MultiModalConversationParam.builder()
                .apiKey(dashScopeConfig.getApiKey())
                .model(dashScopeConfig.getModelImageInstruct())
                .messages(Collections.singletonList(message))
                .incrementalOutput(true)
                .build();
    }

    /**
     * 将用户的消息添加到 Redis 中的对话历史
     *
     * @param prompt
     * @param value
     * @param userId
     */
    private void saveToRedisChatHistory(String prompt, String value, Long userId) {
        // 创建一个消息对象
        Message message = new Message();
        message.setRole(value);
        message.setContent(prompt);
        // 获取当前日期
        String formattedDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String key = CHAT_HISTORY_KEY_PREFIX + userId + ":chatHistory" + ":" + formattedDate;
        // 获取历史记录
        ArrayDeque<Message> deque = getRedisChatHistory(userId);
        //判断是否存在
        if (deque == null) {
            deque = new ArrayDeque<>();
        }
        deque.add(message);
        // 如果超过3条，移除最早添加的元素
        while (deque.size() > 3) {
            deque.pollFirst();
        }

        // 将历史记录保存到redis中并且设置过期时间为12个小时
        redisTemplate.opsForValue().set(key, deque , 12, TimeUnit.HOURS);
    }

    /**
     * 从 Redis 获取该用户的对话历史（用于上下文）
     * @param userId
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private ArrayDeque<Message> getRedisChatHistory(Long userId) {
        //获取当前日期
        String formattedDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String key = CHAT_HISTORY_KEY_PREFIX + userId + ":chatHistory" + ":" + formattedDate;
        return (ArrayDeque<Message>) redisTemplate.opsForValue().get(key);
    }

    /**
     * 处理生成器返回的结果
     * @param result
     * @param emitter
     * @param aiResponseBuilder
     */
    private void processResult(GenerationResult result, FluxSink<String> emitter, StringBuilder aiResponseBuilder) {
        try {
            // 解析响应结构
            String content = result.getOutput()
                    .getChoices()
                    .get(0)
                    .getMessage()
                    .getContent();

            if (content != null && !content.isEmpty()) {
                aiResponseBuilder.append(content); // 将每段回复追加到构建器中
                emitter.next(content);
            }

        } catch (Exception e) {
            emitter.error(new RuntimeException("解析响应失败: " + e.getMessage()));
        }
    }

    /**
     * 保存回答
     *
     * @param aiContent AI回复的内容
     * @param prompt    用户的提示
     * @param userId    用户ID
     */
    @Transactional(rollbackFor = Exception.class)
    private void saveAnswer(String aiContent, String prompt, String[] imgs, String audio, Long userId) {
        // 创建回答的实体
        AiAnswer aiAnswer = new AiAnswer();
        aiAnswer.setUserId(userId);
        aiAnswer.setUserContent(prompt);
        aiAnswer.setAiContent(aiContent);
        String currentDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        aiAnswer.setCreateTime(currentDate);
        if(imgs != null)
        {
            //循环遍历图片使用,进行拼接
            String img = Arrays.stream(imgs).collect(Collectors.joining(","));
            aiAnswer.setImage(img);
        }
        // 保存回答
        chatMapper.insert(aiAnswer);
    }

    /**
     * 获取用户id
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private Long getUserId() {
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        //判断是否登录
        if(loginUser == null)
        {
            throw new BaseException(ResultCodeEnum.TOKEN_INVALID);
        }
        return loginUser.getUserId();
    }

}
